package com.css.zfzx.fxpg.modules.levelmap.economicLoss.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.css.bpm.platform.utils.*;
import com.css.zfzx.fxpg.common.utils.AreaBoundaryUtil;
import com.css.zfzx.fxpg.common.utils.BeanUtil;
import com.css.zfzx.fxpg.common.utils.CheckObjFields;
import com.css.zfzx.fxpg.common.utils.VectorDataUtil;
import com.css.zfzx.fxpg.modules.boundary.Point;
import com.css.zfzx.fxpg.modules.boundary.repository.BoundaryNativeRepository;
import com.css.zfzx.fxpg.modules.boundary.repository.entity.BoundaryEntity;
import com.css.zfzx.fxpg.modules.boundary.viewobjects.BoundaryQueryParams;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.repository.FxEconomicLossLevelMapImport;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.repository.FxEconomicLossLevelMapNativeRepository;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.repository.FxEconomicLossLevelMapRepository;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.repository.entity.FxEconomicLossLevelMapEntity;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.service.FxEconomicLossLevelMapService;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.viewobjects.FxEconomicLossLevelMapQueryParams;
import com.css.zfzx.fxpg.modules.levelmap.economicLoss.viewobjects.FxEconomicLossLevelMapVo;
import com.css.zfzx.fxpg.modules.popgdp.repository.FxPopGDPNativeRepository;
import com.css.zfzx.fxpg.modules.popgdp.repository.entity.FxPopGDPEntity;
import com.css.zfzx.fxpg.modules.popgdp.viewobjects.FxPopGDPQueryParams;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * @author andy
 * @date 2021-01-26
 */
@Service
@Slf4j
public class FxEconomicLossLevelMapServiceImpl implements FxEconomicLossLevelMapService {

	@Autowired
    private FxEconomicLossLevelMapRepository fxEconomicLossLevelMapRepository;
    @Autowired
    private FxEconomicLossLevelMapNativeRepository fxEconomicLossLevelMapNativeRepository;

    @Autowired
    private FxEconomicLossLevelMapImport fxEconomicLossLevelMapImport;

    @Autowired
    private FxPopGDPNativeRepository fxPopGDPNativeRepository;

    @Override
    public JSONObject queryFxEconomicLossLevelMaps(FxEconomicLossLevelMapQueryParams queryParams, int curPage, int pageSize) {
        Page<FxEconomicLossLevelMapEntity> fxEconomicLossLevelMapPage = this.fxEconomicLossLevelMapNativeRepository.queryFxEconomicLossLevelMaps(queryParams, curPage, pageSize);
        JSONObject jsonObject = PlatformPageUtils.formatPageData(fxEconomicLossLevelMapPage);
        return jsonObject;
    }

    @Override
    public JSONObject queryFxEconomicLossLevelMapsByFileName(FxEconomicLossLevelMapQueryParams queryParams, int curPage, int pageSize, String sort, String order) {
        if (!PlatformObjectUtils.isEmpty(sort) && sort.length() > 0) {
            sort = CheckObjFields.propertyChange(sort);
        }
        Page<FxEconomicLossLevelMapEntity> fxEconomicLossLevelMapPage = this.fxEconomicLossLevelMapNativeRepository.queryFxEconomicLossLevelMapsByFileName(queryParams, curPage, pageSize, sort, order);
        JSONObject jsonObject = PlatformPageUtils.formatPageData(fxEconomicLossLevelMapPage);
        return jsonObject;
    }

    @Override
    public FxEconomicLossLevelMapEntity getFxEconomicLossLevelMap(String id) {
        FxEconomicLossLevelMapEntity fxEconomicLossLevelMap = this.fxEconomicLossLevelMapRepository.findById(id).orElse(null);
        return fxEconomicLossLevelMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FxEconomicLossLevelMapEntity saveFxEconomicLossLevelMap(FxEconomicLossLevelMapEntity fxEconomicLossLevelMap) {
        String uuid = UUIDGenerator.getUUID();
        fxEconomicLossLevelMap.setUuid(uuid);
        fxEconomicLossLevelMap.setCreateUser(PlatformSessionUtils.getUserId());
        fxEconomicLossLevelMap.setCreateTime(PlatformDateUtils.getCurrentTimestamp());
        this.fxEconomicLossLevelMapRepository.save(fxEconomicLossLevelMap);
        return fxEconomicLossLevelMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FxEconomicLossLevelMapEntity updateFxEconomicLossLevelMap(FxEconomicLossLevelMapEntity fxEconomicLossLevelMap) {
        fxEconomicLossLevelMap.setUpdateUser(PlatformSessionUtils.getUserId());
        fxEconomicLossLevelMap.setUpdateTime(PlatformDateUtils.getCurrentTimestamp());
        String id = fxEconomicLossLevelMap.getUuid();
        FxEconomicLossLevelMapEntity one = fxEconomicLossLevelMapRepository.findById(id).get();
        BeanUtil.copyPropertiesIgnoreNull(fxEconomicLossLevelMap, one);
        this.fxEconomicLossLevelMapRepository.save(one);
        return fxEconomicLossLevelMap;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFxEconomicLossLevelMaps(List<String> ids) {
        List<FxEconomicLossLevelMapEntity> fxEconomicLossLevelMapList = this.fxEconomicLossLevelMapRepository.findAllById(ids);
        if(!PlatformObjectUtils.isEmpty(fxEconomicLossLevelMapList) && fxEconomicLossLevelMapList.size() > 0) {
            for(FxEconomicLossLevelMapEntity fxEconomicLossLevelMap : fxEconomicLossLevelMapList) {
                this.fxEconomicLossLevelMapRepository.delete(fxEconomicLossLevelMap);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteFxEconomicLossLevelMapsByFileName(List<String> fileNames) {
        for (int i = 0; i < fileNames.size(); i++) {
            FxEconomicLossLevelMapQueryParams queryParams = new FxEconomicLossLevelMapQueryParams();
            queryParams.setAffiliatedFileName(fileNames.get(i));
            List<FxEconomicLossLevelMapEntity> fxEconomicLossLevelMapList = this.fxEconomicLossLevelMapNativeRepository.listFxEconomicLossLevelMaps(queryParams);
            if(!PlatformObjectUtils.isEmpty(fxEconomicLossLevelMapList) && fxEconomicLossLevelMapList.size() > 0) {
                /*for(QhProbabilityEarthquakeDangerEntity qhSiteClassResult : qhSiteClassResultList) {
                    this.qhSiteClassResultRepository.delete(qhSiteClassResult);
                }*/
                //物理删
                //fxEconomicLossLevelMapImport.sqlBatchDeleteEntities(fxEconomicLossLevelMapList);
                //逻辑删
                fxEconomicLossLevelMapImport.sqlBatchLogicDeleteEntities(fxEconomicLossLevelMapList);
            }
        }
    }

    @Override
    public JSONObject getFxEconomicLossLevelMaps(FxEconomicLossLevelMapQueryParams queryParams) {
        List<FxEconomicLossLevelMapEntity> qhFxEconomicLossLevelMaps = this.fxEconomicLossLevelMapNativeRepository.listFxEconomicLossLevelMaps(queryParams);
        for (FxEconomicLossLevelMapEntity elmEntity : qhFxEconomicLossLevelMaps) {
            String coords = elmEntity.getCoordinate();
            if (coords == null) continue;
            coords = coords.toLowerCase();
            if (coords.indexOf("multipolygon") != -1) {
                coords = coords.substring(coords.indexOf("(((") + 3, coords.lastIndexOf(")))") - 1);
                coords = coords.replace(")), ((", "|");
                coords = coords.replace("), (", "#");
                elmEntity.setCoordinate(coords);
            } else if (coords.indexOf("polygon") != -1) {
                String sp = coords.indexOf("),(") != -1 ? "),(" : (coords.indexOf("), (") != -1 ? "), (" : "");
                String oldWkt = coords, coordinate = "";
                if ("".equals(sp)) {
                    oldWkt = oldWkt.substring(oldWkt.indexOf("(") + 2, oldWkt.lastIndexOf("))"));
                    String[] coordinates = oldWkt.split(",");
                    for (int i = 0; i < coordinates.length; i++) {
                        String[] _coords = coordinates[i].split(" ");
                        coordinate += _coords[0] + " " + _coords[1] + ",";
                    }
                    coordinate = coordinate.substring(0, coordinate.length() - 1);
                    elmEntity.setCoordinate(coordinate);
                } else {
                    oldWkt = oldWkt.replace(sp, "#"); sp = "#";
                    String[] coordinates = oldWkt.split(sp); String tempCoordinate = "";
                    for (int i = 0; i < coordinates.length; i++) {
                        tempCoordinate = coordinates[i];
                        String[] _coords = tempCoordinate.split(","), __coords = null;
                        for (int j = 0; j < _coords.length; j++) {
                            __coords = _coords[j].split(" ");
                            coordinate += __coords[0] + " " + __coords[1] + ",";
                        }
                        coordinate = coordinate.substring(0, coordinate.length() - 1) + "|";
                    }
                    elmEntity.setCoordinate(coordinate);
                }
            }
            //scrEntity.setCoordinates(ProjectTransUtil.parseWktPolygon(scrEntity.getCoordinates()));
        }
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("data", qhFxEconomicLossLevelMaps);
        return jsonObject;
    }

    @Override
    public String importShapeFile(String filePath, String province, String city, String area, String condition) {
        try {
            String fileName = filePath.substring(filePath.lastIndexOf("/") + 1, filePath.length() - 4);
            // 返回的消息
            StringBuilder returnMsg = new StringBuilder();
            List<FxEconomicLossLevelMapVo> scrVOs = new ArrayList<FxEconomicLossLevelMapVo>();
            List<List<Object>> list = VectorDataUtil.readSHPFile(filePath, "UTF-8");
            if (VectorDataUtil.judgeIsHaveMsssyCode(list) != -1) {
                list = VectorDataUtil.readSHPFile(filePath, "GBK");
                int msssyIndex = VectorDataUtil.judgeIsHaveMsssyCode(list);
                if (msssyIndex != -1)  return "messyCode#" + msssyIndex;
            }
            if (!(VectorDataUtil.checkValueIsBlank(list))) return "fieldNullValue";
            String judgeResult = judgeFieldParams(list);
            if (!("".equals(judgeResult))) return judgeResult + "上传数据失败, 请检查数据正确性!";
            if (!(VectorDataUtil.checkLevel(list))) return "levelError";

            String checkResult = checkPointIsInDistrict(province, city, area, list);
            if (!"true".equals(checkResult)) return getErrorInfo(checkResult, province, city, area);

            /*String lsGDp = getGDPOfArea(province, city, area);
            if (lsGDp == null) return "获取" + province + city + area + "GDP数据失败, 请检查GDP数据是否存在!";*/
            int size = 0;
            for (int i = 0; i < list.size(); i++) {
                List<Object> values = list.get(i);
                size = values.size();
                FxEconomicLossLevelMapVo elmVO = new FxEconomicLossLevelMapVo();
                elmVO.setProvince(province);
                elmVO.setCity(city);
                elmVO.setArea(area);
                elmVO.setAffiliatedFileName(fileName);
                elmVO.setExtend10(condition);//地震危险性 0:50年超越概率2%分区, 1:50年超越概率10%分区, 2:50年超越概率63%分区, 3:100年超越概率1%分区
                //elmVO.setExtend9(String.valueOf(lossNum));//(tif文件中直接经济损失)
                //elmVO.setExtend8(lsGDp);//tif文件中GDP)
                //double ratio = lossNum / Double.parseDouble(lsGDp);
                //elmVO.setExtend7(String.format("%.4f", ratio));//(tif文件中直接经济损失<extend9>/tif文件中GDP<extend8>)
                //elmVO.setExtend6(String.valueOf(VectorDataUtil.getRiskLevelByTIFValue(ratio, "0")));//(extend7对应的风险等级)
                elmVO.setCoordinate(values.get(0).toString());
                elmVO.setLevel(Integer.parseInt(values.get(1).toString()));
                if (size >= 3) elmVO.setExtend1(values.get(2).toString());
                if (size >= 4) elmVO.setExtend2(values.get(3).toString());
                if (size >= 5) elmVO.setExtend3(values.get(4).toString());
                if (size >= 6) elmVO.setExtend4(values.get(5).toString());
                if (size >= 7) elmVO.setExtend5(values.get(6).toString());

                scrVOs.add(elmVO);
            }
            // 批量新增
            this.saveData(scrVOs);
            returnMsg.append("成功导入" + scrVOs.size() + "条数据.");
            return returnMsg.toString();
        } catch (RuntimeException e) {
            log.error(e.getMessage(), e);
            return "插入或更新数据出现错误: <br/>" + e.getMessage();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return "数据导入失败, 服务器内部出现异常!";
        }
    }

    private String getGDPOfArea (String province, String city, String area) {
        FxPopGDPQueryParams queryParams = new FxPopGDPQueryParams();
        queryParams.setProvince(province);
        queryParams.setCity(city);
        queryParams.setArea(area);
        List<FxPopGDPEntity> pgs = fxPopGDPNativeRepository.getFxPopGDPs(queryParams, null);
        if (pgs.size() == 1) return pgs.get(0).getLs_GDP();
        return null;
    }

    private void saveData(List<FxEconomicLossLevelMapVo> list) throws Exception {
        List<FxEconomicLossLevelMapEntity> elms = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            FxEconomicLossLevelMapEntity rlmEntity = new FxEconomicLossLevelMapEntity();
            FxEconomicLossLevelMapVo rlmVO = list.get(i);
            // 将VO数据映射拷贝到库表中
            BeanUtils.copyProperties(rlmVO, rlmEntity);
            rlmEntity.setIsValid("1");
            rlmEntity.setExtend9("CGCS2000");
            elms.add(rlmEntity);
            //saveOrUpdate(scrEntity);
        }
        fxEconomicLossLevelMapImport.sqlBatchSaveEntities(elms);
    }

    @Override
    public void saveOrUpdate(FxEconomicLossLevelMapEntity elmEntity) {
        FxEconomicLossLevelMapQueryParams queryParams = new FxEconomicLossLevelMapQueryParams();
        queryParams.setProvince(elmEntity.getProvince());
        queryParams.setCity(elmEntity.getCity());
        queryParams.setArea(elmEntity.getArea());
        List<FxEconomicLossLevelMapEntity> elmEntities = fxEconomicLossLevelMapNativeRepository.listFxEconomicLossLevelMaps(queryParams);
        // 若查到数据为空，则新增
        if (CollectionUtils.isEmpty(elmEntities)) {
            this.saveFxEconomicLossLevelMap(elmEntity);
        } else {
            if (elmEntities.size() == 1) {
                FxEconomicLossLevelMapEntity sourceProvinceEntity = elmEntities.get(0);
                BeanUtil.copyPropertiesIgnoreNull(elmEntity, sourceProvinceEntity);
                this.updateFxEconomicLossLevelMap(sourceProvinceEntity);
            } else {
                throw new RuntimeException("库中该数据最多一条，请管理员清理数据库冗余数据！");
            }
        }
    }

    @Override
    public String judgeFileParams(String[] datas) {
        String str = "";
        Set<String> set = new HashSet<String>(Arrays.asList(datas));
        if(!set.contains("A")){
            str += "A,";
        }
        if(!set.contains("B")){
            str += "B,";
        }
        if(!set.contains("C")){
            str += "C,";
        }
        return str;
    }

    private String judgeFieldParams(List<List<Object>> list) {
        String str = "";
        for (int i = 0; i < list.size(); i++) {
            List<Object> values = list.get(i);
            //for (int j = 0; j < values.size(); j++) {
            for (int j = 0; j < 2; j++) {
                if ("".equals(values.get(j))) {
                    str += "第 " + i + " 条数据第 " + j + " 个字段数据为空.<br/>";
                }
            }
        }
        return str;
    }

    @Autowired
    private BoundaryNativeRepository boundaryNativeRepository;
    private double GLOBAL_THRESH = 1E-8;
    private String checkPointIsInDistrict(String province, String city, String area, List<List<Object>> list) {
        BoundaryQueryParams queryParams = new BoundaryQueryParams();
        queryParams.setType("1");
        queryParams.setLevel("2");
        queryParams.setProvince(province);
        queryParams.setCity(city);
        queryParams.setArea(area);
        queryParams.setName(area);
        List<BoundaryEntity> boundaryEntities = this.boundaryNativeRepository.listBoundaries(queryParams);
        if (boundaryEntities.size() == 1) {
            BoundaryEntity boundaryEntity = boundaryEntities.get(0);
            String coordinate = boundaryEntity.getCoordinate();
            if (coordinate == null || "".equals(coordinate)) {
                return "NoDistrictBoundaryData";
            } else {
                coordinate = coordinate.substring(coordinate.indexOf("(((") + 3, coordinate.lastIndexOf(")))"));
                coordinate = coordinate.replace(")), ((", "|");
                coordinate = coordinate.replace("), (", "#");
                List<Point[]> boundaries = new ArrayList<>();
                String[] coordinates = StringUtils.split(coordinate, "|");
                for (int j = 0; j < coordinates.length; j++) {
                    String[] subGeometries = coordinates[j].split("#");
                    for (int k = 0; k < subGeometries.length; k++) {
                        String[] lonLats = subGeometries[k].split(", ");
                        Point[] points = new Point[lonLats.length];
                        for (int i = 0; i < lonLats.length; i++) {
                            String[] lonLat = lonLats[i].split(" ");
                            Point point = new Point();
                            point.x = Double.parseDouble(lonLat[0]);
                            point.y = Double.parseDouble(lonLat[1]);
                            points[i] = point;
                        }
                        boundaries.add(points);
                    }
                }

                int flag = 0;
                String wktCoordinate = "";
                for (int i = 0; i < list.size(); i++) {
                    List<Object> attributes = list.get(i);
                    wktCoordinate = attributes.get(0).toString();
                    List<List<Point[]>> geometries = parseWktCoordinate(wktCoordinate);
                    for (int j = 0; j < geometries.size(); j++) {
                        List<Point[]> subGeometries = geometries.get(j);
                        for (int k = 0; k < subGeometries.size(); k++) {
                            Point[] subGeometryPoints = subGeometries.get(k);
                            for (int l = 0; l < subGeometryPoints.length; l++) {
                                Point subGeometryPoint = subGeometryPoints[l];

                                flag = 0;
                                for (int m = 0; m < boundaries.size(); m++) {
                                    Point[] boundary = boundaries.get(m);
                                    if (AreaBoundaryUtil.isPointInPolygon(subGeometryPoint, boundary)) {
                                        flag++;
                                        break;
                                    } else {
                                        double minDis = Double.MAX_VALUE;
                                        for (int n = 0; n < boundary.length - 1; n++) {
                                            Point p1 = boundary[n], p2 = boundary[n + 1];
                                            double d = AreaBoundaryUtil.pointToLine(p1.x, p1.y, p2.x, p2.y, subGeometryPoint.x, subGeometryPoint.y);
                                            if (minDis > d) minDis = d;
                                        }
                                        if (minDis < GLOBAL_THRESH) {
                                            flag++;
                                            break;
                                        }
                                    }
                                }
                                if (flag == 0) return "false";

                            }
                        }
                    }
                }
            }
        } else if (boundaryEntities.size() == 0) {
            return "NoDistrictBoundaryData";
        } else {
            return "DistrictBoundaryDataRedundance";
        }
        return "true";
    }
    private String getErrorInfo (String info, String province, String city, String area) {
        if ("NoDistrictBoundaryData".equals(info)) return "不存在【" + province + city + area + "】边界数据, 入库失败!";
        if ("DistrictBoundaryDataRedundance".equals(info)) return "【" + province + city + area + "】边界数据冗余, 入库失败!";
        if ("false".equals(info)) return "存在不在【" + province + city + area + "】边界范围内的数据, 入库失败!";
        return "错误信息未知!";
    }
    private List<List<Point[]>> parseWktCoordinate(String wktCoordinate) {

        List<List<Point[]>> geometriesList = new ArrayList<>();

        wktCoordinate = wktCoordinate.substring(wktCoordinate.indexOf("(((") + 3, wktCoordinate.lastIndexOf(")))"));
        wktCoordinate = wktCoordinate.replace(")), ((", "|");
        wktCoordinate = wktCoordinate.replace("), (", "#");
        if (wktCoordinate.indexOf(")#") != -1) wktCoordinate = wktCoordinate.replace(")#", "|");

        String[] geometries = StringUtils.split (wktCoordinate, "|");
        for (int i = 0; i < geometries.length; i++) {
            List<Point[]> subGeometriesList = new ArrayList<>();
            String[] subGeometries = StringUtils.split (geometries[i], "#");
            for (int j = 0; j < subGeometries.length; j++) {
                String[] coords = subGeometries[j].split(", ");
                Point[] subGeometryPoints = new Point[coords.length];
                for (int k = 0; k < coords.length; k++) {
                    String[] lonLat = coords[k].split(" ");
                    double lon = Double.parseDouble(lonLat[0]), lat = Double.parseDouble(lonLat[1]);
                    Point point = new Point(lon, lat);
                    subGeometryPoints[k] = point;
                }
                subGeometriesList.add(subGeometryPoints);
            }
            geometriesList.add(subGeometriesList);
        }
        return geometriesList;
    }

    @Override
    public Long lookUpFxEconomicLossLevelMaps(FxEconomicLossLevelMapQueryParams queryParams) {
        Long count = this.fxEconomicLossLevelMapNativeRepository.lookUpFxEconomicLossLevelMaps(queryParams);
        return count;
    }

}
